// 数据库连接配置

package daos

import (
	"context"
	"fmt"
	"os"
	"sync"
	"time"

	"bsm/config"
	"bsm/logger"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"github.com/redis/go-redis/v9"
	_ "github.com/sijms/go-ora/v2"
)

var (
	redisClient *redis.Client
	redisMutex  sync.Mutex
)

// DatabaseConfig 包含数据库连接配置
type DatabaseConfig struct {
	User     string
	Password string
	Host     string
	Port     string
	DBName   string
}

// NewDatabaseConfigFromEnv 从环境变量创建配置
func NewDatabaseConfigFromEnv(prefix string) DatabaseConfig {
	return DatabaseConfig{
		User:     os.Getenv(prefix + "USER"),
		Password: os.Getenv(prefix + "PASSWORD"),
		Host:     os.Getenv(prefix + "HOST"),
		Port:     os.Getenv(prefix + "PORT"),
		DBName:   os.Getenv(prefix + "DBNAME"),
	}
}

// InitPmsDatabase 初始化单个数据库连接
func InitPmsDatabase(cfg DatabaseConfig) (*sqlx.DB, error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DBName)

	db, err := sqlx.Connect("mysql", dsn)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "daos",
			"operation": "InitPmsDatabase",
			"error":     err.Error(),
		}).Errorf("failed to connect database: %v", err)
		return nil, fmt.Errorf("failed to connect database: %w", err)
	}

	db.SetMaxOpenConns(100)
	db.SetMaxIdleConns(10)
	db.SetConnMaxLifetime(5 * time.Minute)

	return db, nil
}

// InitScmDatabase 初始化单个数据库连接
func InitScmDatabase(cfg DatabaseConfig) (*sqlx.DB, error) {
	dsn := fmt.Sprintf("oracle://%s:%s@%s:%s?sid=%s", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DBName)
	db, err := sqlx.Connect("oracle", dsn)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "daos",
			"operation": "InitScmDatabase",
			"error":     err.Error(),
		}).Errorf("failed to connect database: %v", err)
		return nil, fmt.Errorf("failed to connect database: %w", err)
	}

	db.SetMaxOpenConns(100)
	db.SetMaxIdleConns(10)
	db.SetConnMaxLifetime(5 * time.Minute)

	return db, nil
}

// InitDatabase 初始化所有数据库连接
func InitDatabase() (*sqlx.DB, *sqlx.DB, error) {
	bsdCfg := NewDatabaseConfigFromEnv("MYSQL_BSD_")
	scmCfg := NewDatabaseConfigFromEnv("ORACLE_SCM_")

	dbBSD, err := InitPmsDatabase(bsdCfg)
	if err != nil {
		dbBSD.Close()
		logger.WithFields(map[string]interface{}{
			"layer":     "daos",
			"operation": "InitDatabase",
			"error":     err.Error(),
		}).Errorf("BSD database init failed: %v", err)
		return nil, nil, fmt.Errorf("BSD database init failed: %w", err)
	}

	dbSCM, err := InitScmDatabase(scmCfg)
	if err != nil {
		dbSCM.Close()
		logger.WithFields(map[string]interface{}{
			"layer":     "daos",
			"operation": "InitDatabase",
			"error":     err.Error(),
		}).Errorf("SCM database init failed: %v", err)
		return nil, nil, fmt.Errorf("SCM database init failed: %w", err)

	}

	logger.WithFields(map[string]interface{}{
		"layer":     "daos",
		"operation": "InitDatabase",
	}).Info("All databases initialized successfully")

	return dbBSD, dbSCM, nil
}

// NewRedisClient 创建新的Redis客户端实例（不设置全局变量）
func NewRedisClient() *redis.Client {
	redisConfig := config.Get().RedisConfig
	if redisConfig.Addr == "" {
		redisConfig.Addr = "localhost:6379"
	}

	return redis.NewClient(&redis.Options{
		Addr:         redisConfig.Addr,
		Password:     redisConfig.Password,
		DB:           redisConfig.DB,
		PoolSize:     redisConfig.PoolSize,
		MinIdleConns: redisConfig.MinIdleConns,
		DialTimeout:  redisConfig.DialTimeout,
		ReadTimeout:  redisConfig.ReadTimeout,
		WriteTimeout: redisConfig.WriteTimeout,
	})
}

// TestRedisConnection 测试Redis连接
func TestRedisConnection(client *redis.Client) error {
	if client == nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "daos",
			"operation": "TestRedisConnection",
			"error":     "redis client is nil",
		}).Errorf("redis client is nil")
		return fmt.Errorf("redis client is nil")
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	_, err := client.Ping(ctx).Result()
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "daos",
			"operation": "TestRedisConnection",
			"error":     err.Error(),
		}).Errorf("无法连接到Redis: %v", err)
		return fmt.Errorf("无法连接到Redis: %w", err)
	}
	return nil
}

// InitRedis 初始化全局Redis客户端
func InitRedis() error {
	redisMutex.Lock()
	defer redisMutex.Unlock()

	if redisClient != nil {
		// 测试现有连接是否仍然有效
		if err := TestRedisConnection(redisClient); err == nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "daos",
				"operation": "InitRedis",
			}).Info("Redis连接已经初始化且有效")
			return nil
		}
		// 连接已失效，重新创建
		logger.WithFields(map[string]interface{}{
			"layer":     "daos",
			"operation": "InitRedis",
		}).Warn("现有Redis连接已失效，重新初始化...")
	}

	client := NewRedisClient()
	if err := TestRedisConnection(client); err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "daos",
			"operation": "InitRedis",
			"error":     err.Error(),
		}).Errorf("Redis连接测试失败: %v", err)
		// 仍然设置全局变量，但标记为不可用
		redisClient = client
		return err
	}

	redisClient = client
	logger.WithFields(map[string]interface{}{
		"layer":     "daos",
		"operation": "InitRedis",
	}).Info("Redis连接初始化成功")
	return nil
}

// GetRedisClient 获取Redis客户端
func GetRedisClient() *redis.Client {
	redisMutex.Lock()
	defer redisMutex.Unlock()
	return redisClient
}

// SafeGetRedisClient 安全获取Redis客户端，如果不可用则返回nil
func SafeGetRedisClient() *redis.Client {
	client := GetRedisClient()
	if client == nil {
		return nil
	}

	// 快速检查连接状态
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	_, err := client.Ping(ctx).Result()
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "daos",
			"operation": "SafeGetRedisClient",
			"error":     err.Error(),
		}).Warnf("Redis连接检查失败: %v", err)
		return nil
	}

	return client
}

func CloseRedisClient() {
	if redisClient != nil {
		if err := redisClient.Close(); err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "daos",
				"operation": "CloseRedisClient",
				"error":     err.Error(),
			}).Errorf("关闭Redis连接时出错: %v", err)
		} else {
			logger.WithFields(map[string]interface{}{
				"layer":     "daos",
				"operation": "CloseRedisClient",
			}).Info("Redis连接已成功关闭")
		}
	}
}
